﻿using Hx.ADSyncPlatform.ActiveDirectory.Access;
using Hx.ADSyncPlatform.ActiveDirectory.Api;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using Hx.ADSyncPlatform.ActiveDirectory.Service;
using Hx.ADSyncPlatform.Infrastructure.Exceptions;
using Masuit.Tools;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Hx.ADSyncPlatform.ActiveDirectory
{
    public class ActiveDirectoryClient
    {
        internal string ADPath { get; set; }

        internal string Account { get; set; }

        internal string Password { get; set; }

        public UserApi userApi { get; private set; }

        private ADConnection ADCInfo;

        public Action<string, List<string>> BeforeCreatePrivateComputer;

        public OrganizationService Organizations { get; private set; }

        public GroupService Groups { get; private set; }

        public PersonService Persons { get; private set; }

        public DomainControllerService Domains { get; private set; }

        public ComputerService Computers { get; private set; }


        List<string> prefixs;
        int suffixMin;
        int suffixMax;

        public ActiveDirectoryClient(string ADPath, string Account, string Password)
        {
            this.ADCInfo = new ADConnection(ADPath, Account, Password);

            Init();
        }

        private void Init()
        {
            this.Organizations = new OrganizationService(this.ADCInfo);
            this.Groups = new GroupService(this.ADCInfo);
            this.Persons = new PersonService(this.ADCInfo);
            this.Domains = new DomainControllerService(this.ADCInfo);
            this.Computers = new ComputerService(this.ADCInfo);

            this.prefixs = new string[] { "hd", "ds", "pd", "jxl" }.ToList();
            this.suffixMax = 99;
            this.suffixMin = 1;
        }


        #region 计算机相关



        public void CreatePersonalComputer(Person adPerson, ConfigItem personalComputerConfig)
        {
            var (newComputerName, personalComputerNames) = this.generateComputerName(adPerson);
            personalComputerNames.Add(newComputerName);

            if (this.BeforeCreatePrivateComputer != null)
            {
                this.BeforeCreatePrivateComputer(adPerson.SamAccountName, personalComputerNames);
            }
            newComputerName = newComputerName.Replace(".", "-");
            //先创建计算机
            Computer computer = new Computer();
            computer.Name = newComputerName;
            computer.CN = newComputerName;
            computer.Info = adPerson.SamAccountName;
            computer.SamAccountName = (newComputerName + "$").ToUpper();
            this.Computers.CreateComputer(adPerson.ParentOU, computer, personalComputerConfig);
            //将计算机添加至用户属性【UserWorkstations】
            adPerson.UserWorkstations = string.Join(",", personalComputerNames);
            this.Persons.Update(adPerson);

        }

        public void CreatePersonalComputer(string computerName, Person adPerson, ConfigItem personalComputerConfig)
        {
            //var (newComputerName, personalComputerNames) = this.generateComputerName(adPerson);
            //personalComputerNames.Add(newComputerName);
            List<string> personalComputerNames = new List<string>();  //计算机列表
            computerName = computerName.Replace(".", "-");
            personalComputerNames = !string.IsNullOrEmpty(adPerson.UserWorkstations) ? adPerson.UserWorkstations.Split(",").ToList() : personalComputerNames;
            personalComputerNames.Add(computerName);
            if (this.BeforeCreatePrivateComputer != null)
            {
                this.BeforeCreatePrivateComputer(adPerson.SamAccountName, personalComputerNames);
            }

            //先创建计算机
            Computer computer = new Computer();
            computer.Name = computerName;
            computer.CN = computerName;
            computer.Info = adPerson.SamAccountName;
            computer.SamAccountName = (computerName + "$").ToUpper();
            this.Computers.CreateComputer(adPerson.ParentOU, computer, personalComputerConfig);
            //将计算机添加至用户属性【UserWorkstations】
            if (personalComputerNames.Count == 0)
                adPerson.UserWorkstations = null;
            else
                adPerson.UserWorkstations = string.Join(",", personalComputerNames);
            this.Persons.Update(adPerson);

        }

        public (string newComputerName, List<string> personalComputerNames) generateComputerName(Person adPerson)
        {
            string personAccount = adPerson.SamAccountName;
            if (personAccount.Length > 13)
            {
                personAccount = personAccount.Substring(0, 13);
            }
            string computerName = personAccount + "c";
            computerName = computerName.Replace(".", "-");
            int serialNum = 1;//序号
            string workStations = adPerson.UserWorkstations;
            List<string> computerNames = new List<string>();  //计算机列表
            if (!string.IsNullOrEmpty(workStations))//计算最大的序号
            {
                computerNames = adPerson.UserWorkstations.Split(",").ToList();
                //私有计算机
                List<string> personalComputerNames = computerNames.Where(n => n.IndexOf(computerName) == 0).ToList();

                if (personalComputerNames.Any())
                {
                    if (personalComputerNames.Count >= 99)
                    {
                        throw new BizException($"用户{personAccount}已有{personalComputerNames.Count}台私人计算机，无法继续创建！");
                    }
                    //序号查缺补漏 
                    List<int> computerSerials = new List<int>();
                    computerSerials = personalComputerNames.Select
                        (n => Convert.ToInt32(n.Substring(computerName.Length)))
                        .OrderBy(n => n)
                        .ToList();
                    bool isConsecutive = false;
                    for (int i = 0; i < computerSerials.Count; i++)
                    {
                        if (computerSerials[i] != i + 1)
                        {
                            serialNum = i + 1;
                            isConsecutive = true;
                            break;
                        }
                    }
                    if (!isConsecutive)
                    {
                        serialNum = computerSerials.Last() + 1;
                    }

                }

            }

            string newComputerName = computerName + serialNum.ToString();
            Computer computer = this.Computers.Single(newComputerName.ToUpper() + "$");
            while (computer != null)
            {
                if (serialNum >= 99)
                {
                    throw new BizException($"用户{personAccount}已有9台私人计算机，无法继续创建！");
                }
                serialNum++;
                newComputerName = computerName + serialNum.ToString();
                computer = this.Computers.Single(newComputerName.ToUpper() + "$");
            }

            return (newComputerName, computerNames);
        }


        public void CreatePublicComputer(string ou, string computerName, ConfigItem publicComputerConfig)
        {
            if (computerName.Contains("$"))
            {
                throw new BizException("计算机名不能存在$");
            }
            if (computerName.Length > 15)
            {
                throw new BizException("计算机名长度最多15位");
            }
            if (computerName.Contains("."))
            {
                computerName = computerName.Replace(".", "-");
            }
            //先创建计算机
            Computer computer = new Computer();
            computer.Name = computerName;
            computer.CN = computerName;
            computer.SamAccountName = (computerName + "$").ToUpper();
            this.Computers.CreateComputer(ou, computer, publicComputerConfig);
        }

        public void CreateDefaultComputer(Person person, ConfigItem personalComputerConfig)
        {
            string computerName = person.SamAccountName;
            if (computerName.Contains("."))
            {
                computerName = computerName.Replace(".", "-");
            }
            if (computerName.Length > 15)
            {
                computerName = computerName.Substring(0, 15);
            }
            //先创建计算机
            Computer computer = new Computer();
            computer.Name = computerName;
            computer.CN = computerName;
            computer.SamAccountName = (computerName + "$").ToUpper();
            computer.Info = person.SamAccountName;
            this.Computers.CreateComputer(person.ParentOU, computer, personalComputerConfig);
            person.UserWorkstations += ("," + computerName);
            this.Persons.Update(person);
        }


        /// <summary>
        /// 获取用户的其他计算机（排除默认计算机）
        /// </summary>
        /// <param name="personAccount"></param>
        /// <returns></returns>
        public List<string> GetOtherComputers(string personAccount)
        {
            Person adPerson = this.Persons.Single(personAccount);
            string workStations = adPerson.UserWorkstations;

            if (string.IsNullOrEmpty(workStations))
            {
                return null;
            }
            List<string> computers = adPerson.UserWorkstations.Split(",").ToList();
            return this.GetOtherComputers(personAccount, computers);

        }

        /// <summary>
        /// 获取用户的其他计算机（排除默认计算机）
        /// </summary>
        /// <param name="personAccount"></param>
        /// <returns></returns>
        public List<string> GetOtherComputers(string personAccount, List<string> computers)
        {
            if (personAccount.Contains("."))
            {
                personAccount = personAccount.Replace(".", "-");
            }
            if (personAccount.Length > 15)
            {
                personAccount = personAccount.Substring(0, 15);
            }
            List<string> otherComputers = new List<string>(computers.Count);
            foreach (string computerName in computers)
            {
                otherComputers.Add(computerName);//计算机规则修改
                                                 //if (personAccount != computerName)
                                                 //{

                //}
            }
            return otherComputers;
        }

        /// <summary>
        /// 判断是否为遗留计算机
        /// </summary>
        /// <param name="computerName"></param>
        /// <param name="prefixs"></param>
        /// <param name="suffixMin"></param>
        /// <param name="suffixMax"></param>
        /// <returns></returns>
        public bool IsOldComputer(string computerName, string personName = "")
        {
            computerName = computerName.ToLower();
            bool isStartWith = prefixs.Any(prefix => computerName.StartsWith(prefix));

            if (isStartWith) return true;
            //lvzhen-exc1   sadasdsac1
            if (!string.IsNullOrWhiteSpace(personName))
            {
                isStartWith = computerName.StartsWith(personName);
            }
            if (isStartWith) return false;

            int charCount = computerName.Length;
            int lastIndex = computerName.LastIndexOf("c");
            if (lastIndex == -1 || lastIndex > charCount - 1)
            {
                return false;
            }
            string end = computerName.Substring(lastIndex);
            int numberPart = Convert.ToInt32(end.Substring(1));
            if (numberPart >= suffixMin && numberPart <= suffixMax)
            {

                return true;
            }
            return false;

        }



        public bool PrivateOldRule(string computerName, string personName)
        {
            //规则1  hd...开头
            computerName = computerName.ToLower();
            bool isStartWith = prefixs.Any(prefix => computerName.StartsWith(prefix));
            if (isStartWith) return true;

            //规则2 c1---c99后缀
            int charCount = computerName.Length;
            int lastIndex = computerName.LastIndexOf("c");
            if (lastIndex > -1 && lastIndex < charCount - 1)
            {
                string end = computerName.Substring(lastIndex);
                int numberPart = Convert.ToInt32(end.Substring(1));
                if (numberPart >= suffixMin && numberPart <= suffixMax)
                {
                    return true;
                }
            }
            //规则3 计算机名包含用户名前12位
            if (personName.Length > 12) personName = personName.Substring(0, 12);
            if (!string.IsNullOrWhiteSpace(personName))
            {
                isStartWith = computerName.StartsWith(personName);
            }
            if (isStartWith) return true;

            return false;

        }


        public bool PrivateNewRule(string computerName, string personName)
        {
            computerName = computerName.ToLower();
            //规则1 计算机名称==用户名称
            if (computerName == personName) return true;

            //规则2  计算机名称 包含 用户名前12 后缀为c1-c99   lvzhen.exc
            if (personName.Length > 12) personName = personName.Substring(0, 12);
            bool isStartWith = computerName.StartsWith(personName + "c");
            //未找到或者c是最后一位
            if (!isStartWith) return false;

            int endIndex = personName.Length + 1;

            string numberPart = computerName.Substring(endIndex);
            //末尾不是数字
            if (!int.TryParse(numberPart, out int num))
            {
                return false;
            }
            //1-99区间内
            if (num >= suffixMin && num <= suffixMax)
            {
                return true;
            }
            return false;

        }
        public bool IsPrivateComputer(string computerName, string personName, bool useOldRule)
        {
            bool isPrivateComputer = false;
            if (useOldRule)
            {
                isPrivateComputer = PrivateOldRule(computerName, personName);
                if (!isPrivateComputer)//旧规则未生效 继续使用新规则判断
                    isPrivateComputer = PrivateNewRule(computerName, personName);
            }
            else
            {
                isPrivateComputer = PrivateNewRule(computerName, personName);
            }

            return isPrivateComputer;
        }
        public (List<Computer> privateComputers, List<Computer> publicComputers) ResolveComputers(Person person, SystemConfig systemConfig)
        {
            if (string.IsNullOrEmpty(person.UserWorkstations)) return (new List<Computer>(), new List<Computer>());
            //已绑定的计算机名称列表
            List<string> bindedComputerNames = person.UserWorkstations.Split(",").ToList();
            //已绑定的计算机列表 排除不存在的计算机
            List<Computer> computers = bindedComputerNames.Select(n => this.Computers.Single(n + "$")).Where(n => n != null).ToList();

            //返回结果
            List<Computer> privateComputers = new List<Computer>();
            List<Computer> publicComputers = new List<Computer>();
            string personName = person.SamAccountName.ToLower();
            personName = personName.Replace(".", "-");
            string fullPersonName = personName;
            if (personName.Length > 15) personName = personName.Substring(0, 15);

            //人员所在父级组织的OU
            string parentPrivateOuDN = $"OU={ systemConfig.personComputer.name}," + person.ParentOU;
            string parentPubOuDN = $"OU={ systemConfig.publicComputer.name}," + person.ParentOU;
            //私人计算机数据源
            List<Computer> privateDatasource = this.Computers.PrivateComputers(parentPrivateOuDN, systemConfig.personComputer.name, SearchScopeDerivation.OneLevel, person.SamAccountName);
            //公共计算机数据源
            List<Computer> publicDatasource = this.Computers.PublicComputers(parentPubOuDN, systemConfig.publicComputer);

            //判断已绑定的数据
            computers.ForEach(n =>
            {
                Computer ds = this.Computers.Single(n.SamAccountName);
                if (ds != null)//计算机存在
                {
                    ds.Binded = true;
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, true);
                    if (isPrivate)//私人计算机
                    {
                        privateComputers.Add(ds);
                    }
                    else
                    {
                        publicComputers.Add(ds);
                    }
                    //else //公共计算机
                    //{
                    //    n.Binded = true;
                    //    publicComputers.Add(n);//公共计算机只显示已绑定的
                    //}
                }
            });
            //判断未绑定的
            privateDatasource.ForEach(n =>
            {
                Computer ds = computers.Find(d => d.SamAccountName == n.SamAccountName);
                if (ds == null)//未绑定
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, false);
                    if (isPrivate)
                    {
                        privateComputers.Add(n);
                    }
                }
            });

            publicDatasource.ForEach(n =>
            {
                Computer ds = computers.Find(d => d.SamAccountName == n.SamAccountName);
                if (ds == null)
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, false);
                    if (!isPrivate)
                    {
                        publicComputers.Add(n);
                    }
                }

            });

            return (privateComputers, publicComputers);
        }


        public List<Computer> GetPrivateComputers(Person person, SystemConfig systemConfig)
        {
            if (string.IsNullOrEmpty(person.UserWorkstations)) return new List<Computer>();
            //已绑定的计算机名称列表
            List<string> bindedComputerNames = person.UserWorkstations.Split(",").ToList();
            //已绑定的计算机列表 排除不存在的计算机
            List<Computer> computers = bindedComputerNames.Select(n => this.Computers.Single(n + "$")).Where(n => n != null).ToList();

            //返回结果
            List<Computer> privateComputers = new List<Computer>();
            string personName = person.SamAccountName.ToLower();
            personName = personName.Replace(".", "-");
            string fullPersonName = personName;
            if (personName.Length > 15) personName = personName.Substring(0, 15);

            //人员所在父级组织的OU
            string parentPrivateOuDN = $"OU={ systemConfig.personComputer.name}," + person.ParentOU;
            //私人计算机数据源
            List<Computer> privateDatasource = this.Computers.PrivateComputers(parentPrivateOuDN, systemConfig.personComputer.name, SearchScopeDerivation.OneLevel, person.SamAccountName);
            //公共计算机数据源

            //判断已绑定的数据
            computers.ForEach(n =>
            {
                Computer ds = this.Computers.Single(n.SamAccountName);
                if (ds != null)//计算机存在
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, true);
                    if (isPrivate)//私人计算机
                    {
                        ds.Binded = true;
                        privateComputers.Add(ds);
                    }
                    //else //公共计算机
                    //{
                    //    n.Binded = true;
                    //    publicComputers.Add(n);//公共计算机只显示已绑定的
                    //}
                }
            });
            //判断未绑定的
            privateDatasource.ForEach(n =>
            {
                Computer ds = computers.Find(d => d.SamAccountName == n.SamAccountName);
                if (ds == null)//未绑定
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, false);
                    if (isPrivate)
                    {
                        privateComputers.Add(n);
                    }
                }
            });


            return privateComputers;
        }


        public List<Computer> GetPublicComputers(Person person, SystemConfig systemConfig)
        {
            if (string.IsNullOrEmpty(person.UserWorkstations)) return new List<Computer>();
            //已绑定的计算机名称列表
            List<string> bindedComputerNames = person.UserWorkstations.Split(",").ToList();
            //已绑定的计算机列表 排除不存在的计算机
            List<Computer> computers = bindedComputerNames.Select(n => this.Computers.Single(n + "$")).Where(n => n != null).ToList();


            List<Computer> publicComputers = new List<Computer>();
            string personName = person.SamAccountName.ToLower();
            personName = personName.Replace(".", "-");
            string fullPersonName = personName;
            if (personName.Length > 15) personName = personName.Substring(0, 15);

            //人员所在父级组织的OU

            string parentPubOuDN = $"OU={ systemConfig.publicComputer.name}," + person.ParentOU;

            //公共计算机数据源
            List<Computer> publicDatasource = this.Computers.PublicComputers(parentPubOuDN, systemConfig.publicComputer);

            //判断已绑定的数据
            computers.ForEach(n =>
            {
                Computer ds = this.Computers.Single(n.SamAccountName);
                if (ds != null)//计算机存在
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, true);
                    if (!isPrivate)//公共计算机
                    {
                        n.Binded = true;
                        publicComputers.Add(n);//公共计算机只显示已绑定的
                    }

                }
            });

            publicDatasource.ForEach(n =>
            {
                Computer ds = computers.Find(d => d.SamAccountName == n.SamAccountName);
                if (ds == null)
                {
                    bool isPrivate = IsPrivateComputer(n.ComputerName, personName, false);
                    if (!isPrivate)
                    {
                        publicComputers.Add(n);
                    }
                }

            });

            return publicComputers;
        }


        #endregion
    }
}
